#ifndef __FH_SPI_H__
#define __FH_SPI_H__

#include <common.h>
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#if defined CONFIG_FH_AXI_DMA
#include <fh_axi_dma.h>
#elif defined CONFIG_FH_MC_DMA
#include <fh_mc_dma.h>
#else
#include <fh_dma.h>
#endif


#define FH_SPI_DEBUG(...)
#define FH_SPI_ASSERT_FMT "FH_SPI_ASSERT "
#define FH_SPI_ASSERT(cond) do { \
	if (!(cond)) {\
		printf(FH_SPI_ASSERT_FMT "%s:%d\n", __func__, __LINE__); \
		BUG(); \
	} \
} while (0)

#define SPI_CTRL0_OFFSET                    (0x00)
#define SPI_CTRL1_OFFSET                    (0x04)
#define SPI_SSIENR_OFFSET                   (0x08)
#define SPI_MWCR_OFFSET                     (0x0c)
#define SPI_SER_OFFSET                      (0x10)
#define SPI_BAUD_OFFSET                     (0x14)
#define SPI_TXFTL_OFFSET                    (0x18)
#define SPI_RXFTL_OFFSET                    (0x1c)
#define SPI_TXFL_OFFSET                     (0x20)
#define SPI_RXFL_OFFSET                     (0x24)
#define SPI_STATUS_OFFSET                   (0x28)
#define SPI_IMR_OFFSET                      (0x2c)
#define SPI_ISR_OFFSET                      (0x30)
#define SPI_RISR_OFFSET                     (0x34)
#define SPI_TXOIC_OFFSET                    (0x38)
#define SPI_RXOIC_OFFSET                    (0x3c)
#define SPI_RXUIC_OFFSET                    (0x40)
#define SPI_MSTIC_OFFSET                    (0x44)
#define SPI_INTCLR_OFFSET                   (0x48)
#define SPI_DMACTRL_OFFSET                  (0x4c)
#define SPI_DMATDL_OFFSET                   (0x50)
#define SPI_DMARDL_OFFSET                   (0x54)
#define SPI_IDR_OFFSET                      (0x58)
#define SPI_SSI_COMPVER_OFFSET              (0x5c)
#define SPI_DATA_OFFSET                     (0x60)
#define SPI_SAMPLE_DELAY                    (0xf0)

//spic new add reg.
#define SPI_CCFGR_OFFSET                    (0xf4)
#define SPI_OPCR_OFFSET                     (0xf8)
#define SPI_TIMCR_OFFSET                    (0xfc)
#define SPI_BBAR0_OFFSET                    (0x100)
#define SPI_BBAR1_OFFSET                    (0x104)
#define SPI_NFC_CMD_MODE_OFFSET             (0x108)
#define SPI_NFC_ACCESS_START_OFFSET         (0x10c)
#define SPI_NFC_CMD_SET0_OFFSET             (0x110)
#define SPI_NFC_CMD_SET1_OFFSET             (0x114)
#define SPI_NFC_CMD_SET2_OFFSET             (0x118)
#define SPI_NFC_ECC_INFO0_OFFSET            (0x11c)
#define SPI_NFC_ECC_INFO1_OFFSET            (0x120)
#define SPI_NFC_OTH_SET_OFFSET              (0x124)
#define SPI_ECC_REG0_OFFSET                 (0x128)
#define SPI_ECC_REG1_OFFSET                 (0x12c)
#define SPI_ECC_STATUS_OFFSET               (0x130)

#define SPI_INTERVAL                        (0x100000)
#define REG_SPI_CTRLR0(n)                  	(n + SPI_CTRL0_OFFSET)
#define REG_SPI_CTRLR1(n)                   (n + SPI_CTRL1_OFFSET)
#define REG_SPI_SSIENR(n)                   (n + SPI_SSIENR_OFFSET)
#define REG_SPI_MWCR(n)                     (n + SPI_MWCR_OFFSET)
#define REG_SPI_SER(n)                      (n  + SPI_SER_OFFSET)
#define REG_SPI_BAUDR(n)                    (n  + SPI_BAUD_OFFSET)
#define REG_SPI_TXFTLR(n)                   (n  + SPI_TXFTL_OFFSET)
#define REG_SPI_RXFTLR(n)                   (n  + SPI_RXFTL_OFFSET)
#define REG_SPI_TXFLR(n)                    (n + SPI_TXFL_OFFSET)
#define REG_SPI_RXFLR(n)                    (n  + SPI_RXFL_OFFSET)
#define REG_SPI_SR(n)                       (n  + SPI_STATUS_OFFSET)
#define REG_SPI_IMR(n)                      (n  + SPI_IMR_OFFSET)
#define REG_SPI_ISR(n)                      (n  + SPI_ISR_OFFSET)
#define REG_SPI_RISR(n)                     (n  + SPI_RISR_OFFSET)
#define REG_SPI_TXOICR(n)                   (n  + SPI_TXOIC_OFFSET)
#define REG_SPI_RXOICR(n)                   (n  + SPI_RXOIC_OFFSET)
#define REG_SPI_RXUICR(n)                   (n  + SPI_RXUIC_OFFSET)
#define REG_SPI_MSTICR(n)                   (n  + SPI_MSTIC_OFFSET)
#define REG_SPI_ICR(n)                      (n + SPI_INTCLR_OFFSET)
#define REG_SPI_DMACR(n)                    (n  + SPI_DMACTRL_OFFSET)
#define REG_SPI_DMATDLR(n)                  (n  + SPI_DMATDL_OFFSET)
#define REG_SPI_DMARDLR(n)                  (n  + SPI_DMARDL_OFFSET)
#define REG_SPI_IDR(n)                      (n  + SPI_IDR_OFFSET)
#define REG_SPI_SSI_COMP_VERSION(n)         (n  + SPI_SSI_COMPVER_OFFSET)
#define REG_SPI_SSI_DR(n)                   (n  + SPI_DATA_OFFSET)
#define REG_SPI_SSI_SAMPLE_DELAY(n)         (n  + SPI_SAMPLE_DELAY)
//spic new add reg.
#define REG_SPI_CCFGR(n)                    (n  + SPI_CCFGR_OFFSET)
#define REG_SPI_OPCR(n)                     (n  + SPI_OPCR_OFFSET)
#define REG_SPI_TIMCR(n)                    (n  + SPI_TIMCR_OFFSET)
#define REG_SPI_BBAR0(n)                    (n  + SPI_BBAR0_OFFSET)
#define REG_SPI_BBAR1(n)                    (n  + SPI_BBAR1_OFFSET)
#define REG_SPI_NFC_CMD_MODE(n)             (n  + SPI_NFC_CMD_MODE_OFFSET)
#define REG_SPI_NFC_ACCESS_START(n)         (n  + SPI_NFC_ACCESS_START_OFFSET)
#define REG_SPI_NFC_CMD_SET0(n)             (n  + SPI_NFC_CMD_SET0_OFFSET)
#define REG_SPI_NFC_CMD_SET1(n)             (n  + SPI_NFC_CMD_SET1_OFFSET)
#define REG_SPI_NFC_CMD_SET2(n)             (n  + SPI_NFC_CMD_SET2_OFFSET)
#define REG_SPI_NFC_ECC_INFO0(n)            (n  + SPI_NFC_ECC_INFO0_OFFSET)
#define REG_SPI_NFC_ECC_INFO1(n)            (n  + SPI_NFC_ECC_INFO1_OFFSET)
#define REG_SPI_NFC_OTH_SET(n)              (n  + SPI_NFC_OTH_SET_OFFSET)
#define REG_SPI_ECC_REG0(n)                 (n  + SPI_ECC_REG0_OFFSET)
#define REG_SPI_ECC_REG1(n)                 (n  + SPI_ECC_REG1_OFFSET)
#define REG_SPI_ECC_STATUS(n)               (n  + SPI_ECC_STATUS_OFFSET)

#define SPI_IRQ_TXEIS						(lift_shift_bit_num(0))
#define SPI_IRQ_TXOIS						(lift_shift_bit_num(1))
#define SPI_IRQ_RXUIS						(lift_shift_bit_num(2))
#define SPI_IRQ_RXOIS						(lift_shift_bit_num(3))
#define SPI_IRQ_RXFIS						(lift_shift_bit_num(4))
#define SPI_IRQ_MSTIS						(lift_shift_bit_num(5))
#define SPI_STATUS_BUSY						(lift_shift_bit_num(0))
#define SPI_STATUS_TFNF						(lift_shift_bit_num(1))
#define SPI_STATUS_TFE						(lift_shift_bit_num(2))
#define SPI_STATUS_RFNE						(lift_shift_bit_num(3))
#define SPI_STATUS_RFF						(lift_shift_bit_num(4))
#define SPI_STATUS_TXE						(lift_shift_bit_num(5))
#define SPI_STATUS_DCOL						(lift_shift_bit_num(6))
#define SPI_READ_BYTE_TIME_LIMIT			5000
#define SPI_WRITE_BYTE_TIME_LIMIT			5000
#define SPI_WRITE_READ_BYTE_TIME_LIMIT		5000
#define SPI_EEPROM_WREN						0x06
#define SPI_EEPROM_RDSR						0x05
#define SPI_EEPROM_READ						0x03
#define SPI_EEPROM_WRITE					0x02
#ifndef CONFIG_DEFAULT_SPI_BUS
#define CONFIG_DEFAULT_SPI_BUS				0
#endif

#ifndef CONFIG_DEFAULT_SPI_MODE
#define CONFIG_DEFAULT_SPI_MODE 			SPI_MODE_0
#endif

#define SPI_TX_DMA (1 << 1)
#define SPI_RX_DMA (1 << 0)


#define RX_FIFO_MAX_LEN 256
#define RX_FIFO_MIN_LEN 2
#define TX_FIFO_MAX_LEN 256
#define TX_FIFO_MIN_LEN 2
#define SPI_RX_ONLY_ONE_TIME_SIZE	(0x10000)
#define DMA_RX_ONLY_THRESHILD		0x100
#define DMA_RX_LEVEL				(7)
#define SPI_RX_ECC_MAX_SIZE			(2048 + 128)

#ifndef SPI0_DMA_CTL_MASTER_SEL
#define SPI0_DMA_CTL_MASTER_SEL 0
#endif

#ifndef SPI0_MEM_CTL_MASTER_SEL
#define SPI0_MEM_CTL_MASTER_SEL 0
#endif

#ifndef SPI1_DMA_CTL_MASTER_SEL
#define SPI1_DMA_CTL_MASTER_SEL 0
#endif

#ifndef SPI1_MEM_CTL_MASTER_SEL
#define SPI1_MEM_CTL_MASTER_SEL 0
#endif


enum {
	CONFIG_OK = 0,
	CONFIG_PARA_ERROR = lift_shift_bit_num(0),
	//only for the set slave en/disable
	CONFIG_BUSY = lift_shift_bit_num(1),
	//only for write_read mode
	WRITE_READ_OK = 0,
	WRITE_READ_ERROR = lift_shift_bit_num(2),
	WRITE_READ_TIME_OUT = lift_shift_bit_num(3),
	//only for write only mode
	WRITE_ONLY_OK = 0,
	WRITE_ONLY_ERROR = lift_shift_bit_num(4),
	WRITE_ONLY_TIME_OUT = lift_shift_bit_num(5),
	//only for read only mode
	READ_ONLY_OK = 0,
	READ_ONLY_ERROR = lift_shift_bit_num(6),
	READ_ONLY_TIME_OUT = lift_shift_bit_num(7),
	//eeprom mode
	EEPROM_OK = 0,
	EEPROM_ERROR = lift_shift_bit_num(8),
	EEPROM_TIME_OUT = lift_shift_bit_num(9),
	/* if read/write/eeprom error,the error below could give you more
	 * info by reading the 'Spi_ReadTransferError' function
	 */
	MULTI_MASTER_ERROR = lift_shift_bit_num(10),
	TX_OVERFLOW_ERROR = lift_shift_bit_num(11),
	RX_OVERFLOW_ERROR = lift_shift_bit_num(12),
};

//enable spi
typedef enum enum_spi_enable {
	SPI_DISABLE = 0,
	SPI_ENABLE = (lift_shift_bit_num(0)),
} spi_enable_e;

//polarity
typedef enum enum_spi_polarity {
	SPI_POLARITY_LOW = 0,
	SPI_POLARITY_HIGH = (lift_shift_bit_num(7)),
	//bit pos
	SPI_POLARITY_RANGE = (lift_shift_bit_num(7)),
} spi_polarity_e;

//phase
typedef enum enum_spi_phase {
	SPI_PHASE_RX_FIRST = 0,
	SPI_PHASE_TX_FIRST = (lift_shift_bit_num(6)),
	//bit pos
	SPI_PHASE_RANGE = (lift_shift_bit_num(6)),
} spi_phase_e;

//frame format
typedef enum enum_spi_format {
	SPI_MOTOROLA_MODE = 0x00,
	SPI_TI_MODE = 0x10, SPI_MICROWIRE_MODE = 0x20,
	//bit pos
	SPI_FRAME_FORMAT_RANGE = 0x30,
} spi_format_e;

//data size
typedef enum enum_spi_data_size {
	SPI_DATA_SIZE_4BIT = 0x03,
	SPI_DATA_SIZE_5BIT = 0x04,
	SPI_DATA_SIZE_6BIT = 0x05,
	SPI_DATA_SIZE_7BIT = 0x06,
	SPI_DATA_SIZE_8BIT = 0x07,
	SPI_DATA_SIZE_9BIT = 0x08,
	SPI_DATA_SIZE_10BIT = 0x09,
	//bit pos
	SPI_DATA_SIZE_RANGE = 0x0f,
} spi_data_size_e;

//transfer mode
typedef enum enum_spi_transfer_mode {
	SPI_TX_RX_MODE = 0x000,
	SPI_ONLY_TX_MODE = 0x100,
	SPI_ONLY_RX_MODE = 0x200,
	SPI_EEPROM_MODE = 0x300,
	//bit pos
	SPI_TRANSFER_MODE_RANGE = 0x300,
} spi_transfer_mode_e;

//spi baudrate
typedef enum enum_spi_baudrate {
#ifdef SPI_CLK_IN_BAUD
	SPI_SCLKIN = SPI_CLK_IN_BAUD,
#else
#ifdef CONFIG_FH_SPI_DTR
	SPI_SCLKIN = 200000000,
#else
	SPI_SCLKIN = 100000000,
#endif
#endif
	SPI_SCLKOUT_27000000 = (SPI_SCLKIN / 27000000),  //27M
	SPI_SCLKOUT_13500000 = (SPI_SCLKIN / 13500000),  //13.5M
	SPI_SCLKOUT_6000000 = (SPI_SCLKIN / 6000000),  //6.75M
	SPI_SCLKOUT_6750000 = (SPI_SCLKIN / 6750000),  //6.75M
	SPI_SCLKOUT_4500000 = (SPI_SCLKIN / 4500000),	 //4.5M
	SPI_SCLKOUT_3375000 = (SPI_SCLKIN / 3375000),  //3.375M
	SPI_SCLKOUT_2700000 = (SPI_SCLKIN / 2700000),	 //2.7M
	SPI_SCLKOUT_1500000 = (SPI_SCLKIN / 1500000),  //1.5M
	SPI_SCLKOUT_100000 = (SPI_SCLKIN / 100000),  //0.1M
	SPI_SCLKOUT_1000000 = (SPI_SCLKIN / 1000000),  //1M
} spi_baudrate_e;

//spi_irq
typedef enum enum_spi_irq {
	SPI_IRQ_TXEIM = (lift_shift_bit_num(0)),
	SPI_IRQ_TXOIM = (lift_shift_bit_num(1)),
	SPI_IRQ_RXUIM = (lift_shift_bit_num(2)),
	SPI_IRQ_RXOIM = (lift_shift_bit_num(3)),
	SPI_IRQ_RXFIM = (lift_shift_bit_num(4)),
	SPI_IRQ_MSTIM = (lift_shift_bit_num(5)),
	SPI_IRQ_ALL = 0x3f,
} spi_irq_e;

//spi_slave_port
typedef enum enum_spi_slave {
	SPI_SLAVE_PORT0 = (lift_shift_bit_num(0)),
	SPI_SLAVE_PORT1 = (lift_shift_bit_num(1)),
} spi_slave_e;

//dma control
typedef enum enum_spi_dma_control_mode {
	SPI_DMA_RX_POS = (lift_shift_bit_num(0)),
	SPI_DMA_TX_POS = (lift_shift_bit_num(1)),
	//bit pos
	SPI_DMA_CONTROL_RANGE = 0x03,
} spi_dma_control_mode_e;


// read wire mode
typedef enum enum_spi_read_wire_mode {
    STANDARD_READ = 0x00,
    DUAL_OUTPUT   = 0x01,
    DUAL_IO       = 0x02,
    QUAD_OUTPUT   = 0x03,
    QUAD_IO       = 0x04,
} spi_read_wire_mode_e;

// program wire mode
typedef enum enum_spi_prog_wire_mode {
    STANDARD_PROG = 0x00,
    QUAD_INPUT    = 0x01,
} spi_prog_wire_mode_e;

typedef enum enum_spi_frame_form {
    DATA_ONLY = 0x00,
    OP_ONLY   = 0x10,
    OP_1BYTE_ADDR = 0x20,
    OP_2BYTE_ADDR = 0x30,
    OP_3BYTE_ADDR = 0x40,
    OP_4BYTE_ADDR = 0x50,
    NOOP_3BYTE_ADDR = 0x60,
    NOOP_4BYTE_ADDR = 0x70,
} spi_frame_form_e;

typedef enum enum_spi_field_form {
    ALL_SERIAL = 0x00,
    OP_SERIAL_QUAD_DATA = 0x08,
    ALL_DUAL_QUAD = 0x0c,
} spi_field_form_e;

typedef struct {
    spi_read_wire_mode_e read;
    spi_prog_wire_mode_e prog;
} access_wire_mode_s;

// bus base address
enum {
    BUS_BASE_ADDR0 = 0xB0000000,
    BUS_BASE_ADDR1 = 0xB0000000,
};

// ahb Xip config
typedef enum enum_spi_xip_config {
    XIP_DISABLE = 0,
    XIP_ENABLE  = 1,
} spi_xip_config_e;

// ahb DPI config
typedef enum enum_spi_dpi_config {
    DPI_DISABLE = 0,
    DPI_ENABLE  = 1,
} spi_dpi_config_e;

// ahb QPI config
typedef enum enum_spi_qpi_config {
    QPI_DISABLE = 0,
    QPI_ENABLE  = 1,
} spi_qpi_config_e;

#ifdef CONFIG_FH_SPI_ECC
/* SPI ECC Relative Configuration */
enum spi_ecc_data_per_step {
	ECC_PER_1K_BYTE     = 0x0,
	ECC_DATA_STEP_RANGE = 0x6,
};

enum spi_ecc_strength {
	ECC_8BIT_MODE      = 0x0,
	ECC_24BIT_MODE     = 0x8,
	ECC_STRENGTH_RANGE = 0x18,
};

enum spi_ecc_spare_size {
	ECC_SPARE_LENGTH_SHIFT = 6,
	ECC_SPARE_LENGTG_RANGE = 0x3fc0,
};

enum spi_ecc_postion {
	ECC_START_POS_SHIFT = 0,
	ECC_START_POS_RANGE = 0xff,
};

enum spi_page_data_size {
	ECC_PAGE_DATASIZE_2K = 0x00,
	ECC_PAGE_DATASIZE_4K = 0x100,
	ECC_PAGE_DATASIZE_RANGE = 0x300,
};

enum spi_page_spare_size {
	ECC_PAGE_SPARESIZE_SHIFT = 10,
	ECC_PAGE_SPARESIZE_RANGE = 0x7fc00,
};

enum spi_ecc_status {
	ECC_STATUS_SHIFT = 4,
	ECC_STATUS_RANGE = 0x1f0,
};

void spi_hw_ecc_init(struct spi_slave *slave);
#endif

typedef struct _fh_spi_controller {
	unsigned int base;
	unsigned int freq; /* Default frequency */
	unsigned int mode;
	struct spi_slave slave;
	unsigned int data_width;
	unsigned int transfer_mode;
	unsigned int rx_fifo_len;
	unsigned int tx_fifo_len;
	unsigned int rx_hw_hand;
	unsigned int tx_hw_hand;

	unsigned int dma_ctl_master_sel; /*3*/
	unsigned int dma_mem_master_sel; /*1*/
	unsigned int clk_in;
	unsigned int sample_dly;
	unsigned int ecc_status;
	unsigned int dtr_mode;
} fh_spi_controller;

#define SPI_MAX_CS_NO	20

struct s_spi_cs_info{
	unsigned int num;
	unsigned int cs[SPI_MAX_CS_NO];
};

struct s_spi_hand_shake_info{
	unsigned int hw_tx;
	unsigned int hw_rx;
};

struct s_spi_dma_master_info{
	unsigned int dma_ctl_master_sel;
	unsigned int dma_mem_master_sel;
};


static inline fh_spi_controller *to_fh_spi(struct spi_slave *slave)
{
	return container_of(slave, fh_spi_controller, slave);
}

SINT32 Spi_Enable(UINT32 base, spi_enable_e enable);
SINT32 Spi_ReadStatus(UINT32 base);
void SPI_SetRxLevel(UINT32 base, UINT32 level);
#endif
